package com.snowy.framework.util;


import lombok.extern.slf4j.Slf4j;
import okhttp3.*;

import javax.net.ssl.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Slf4j
public class OkHttpUtils {
    private static String DEFAULT_MEDIATYPE = "application/json; charset=utf-8";//默认请求类型
    private static Long DEFAULT_CONNECT_TIMEOUT = 10L;//默认连接超时时间
    private static Long DEFAULT_READ_TIMEOUT = 10L;//默认读取超时时间
    private static int DEFAULT_MAX_RETRY_NUM = 3;//默认最大重试次数

    /**
     * 发起get请求
     * @param url
     * @return
     * @throws IOException
     */
    public static String httpGet(String url) throws Exception {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder().url(url).build();
        Response response = client.newCall(request).execute();
        return response.body().string();
    }

    /**
     * 发起get请求
     * @param url 请求URL
     * @param params 请求参数
     * @param headersParams 请求header
     * @return
     * @throws Exception
     */
    public static String httpGet(String url, Map<String, Object> params, Map<String, String> headersParams) throws Exception{
        // 设置HTTP请求参数
        url += getParams(params);
        Headers headers = setHeaders(headersParams);
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder().url(url).headers(headers).build();
        Response response = client.newCall(request).execute();
        return response.body().string();
    }

    /**
     * 发送httppost请求
     * @param url
     * @param data
     * @return
     */
    public static String httpPost(String url, String data) throws Exception {
        final MediaType JSON = MediaType.parse(DEFAULT_MEDIATYPE);
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(DEFAULT_CONNECT_TIMEOUT, TimeUnit.SECONDS)
                .readTimeout(DEFAULT_READ_TIMEOUT, TimeUnit.SECONDS)
                .build();
        RequestBody body = RequestBody.create(JSON, data);
        Request request = new Request.Builder().url(url).post(body).build();
        Response response = client.newCall(request).execute();
        return response.body().string();
    }

    /**
     * 发送httppost请求
     * @param url
     * @param data
     * @param headersParams 请求头map
     * @return
     */
    public static String httpPost(String url, String data,Map<String, String> headersParams) throws Exception {
        final MediaType JSON = MediaType.parse(DEFAULT_MEDIATYPE);
        Headers headers = setHeaders(headersParams);
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(DEFAULT_CONNECT_TIMEOUT, TimeUnit.SECONDS)
                .readTimeout(DEFAULT_READ_TIMEOUT, TimeUnit.SECONDS)
                .build();
        RequestBody body = RequestBody.create(JSON, data);
        Request request = new Request.Builder()
                .headers(headers)
                .url(url)
                .post(body)
                .build();
        Response response = client.newCall(request).execute();
        return response.body().string();
    }

    /**
     * 发送httppost请求
     * @param url 请求URL
     * @param data 请求参数
     * @param headersParams 请求头map
     * @param mediaType 请求类型
     * @return
     * @throws Exception
     */
    public static String httpPost(String url, String data,Map<String, String> headersParams,String mediaType) throws Exception {
        final MediaType JSON = MediaType.parse(mediaType);
        Headers headers = setHeaders(headersParams);
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(DEFAULT_CONNECT_TIMEOUT, TimeUnit.SECONDS)
                .readTimeout(DEFAULT_READ_TIMEOUT, TimeUnit.SECONDS)
                .build();
        RequestBody body = RequestBody.create(JSON, data);
        Request request = new Request.Builder()
                .headers(headers)
                .url(url)
                .post(body)
                .build();
        Response response = client.newCall(request).execute();
        return response.body().string();
    }

    /**
     * 发送httppost请求
     * @param url 请求URL
     * @param data 请求参数
     * @param mediaType 请求类型
     * @return
     * @throws Exception
     */
    public static String httpPost(String url, String data,String mediaType) throws Exception {
        final MediaType JSON = MediaType.parse(mediaType);
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(DEFAULT_CONNECT_TIMEOUT, TimeUnit.SECONDS)
                .readTimeout(DEFAULT_READ_TIMEOUT, TimeUnit.SECONDS)
                .build();
        RequestBody body = RequestBody.create(JSON, data);
        Request request = new Request.Builder().url(url).post(body).build();
        Response response = client.newCall(request).execute();
        return response.body().string();
    }

    /**
     * 发送httppost请求
     * @param url 请求URL
     * @param data 请求参数
     * @param connectTimeout 连接超时时间
     * @param readTimeout 读取超时时间
     * @return
     * @throws Exception
     */
    public static String httpPost(String url, String data,Long connectTimeout,Long readTimeout) throws Exception {
        final MediaType JSON = MediaType.parse(DEFAULT_MEDIATYPE);
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(connectTimeout, TimeUnit.SECONDS)
                .readTimeout(readTimeout, TimeUnit.SECONDS)
                .build();
        RequestBody body = RequestBody.create(JSON, data);
        Request request = new Request.Builder().url(url).post(body).build();
        Response response = client.newCall(request).execute();
        return response.body().string();
    }

    /**
     * 发送httppost请求
     * @param url 请求URL
     * @param data 请求参数
     * @param headersParams 请求头map
     * @param mediaType 请求类型
     * @return
     * @throws Exception
     */
    public static String httpPost(String url, String data,Map<String, String> headersParams,String mediaType,Long connectTimeout,Long readTimeout) throws Exception {
        final MediaType JSON = MediaType.parse(mediaType);
        Headers headers = setHeaders(headersParams);
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(connectTimeout, TimeUnit.SECONDS)
                .readTimeout(readTimeout, TimeUnit.SECONDS)
                .build();
        RequestBody body = RequestBody.create(JSON, data);
        Request request = new Request.Builder()
                .headers(headers)
                .url(url)
                .post(body)
                .build();
        Response response = client.newCall(request).execute();
        return response.body().string();
    }

    /**
     * 发送httppost请求
     * @param url 请求URL
     * @param data 请求参数
     * @param mediaType 请求类型
     * @param connectTimeout 连接超时时间
     * @param readTimeout 读取超时时间
     * @return
     * @throws Exception
     */
    public static String httpPost(String url, String data,String mediaType,Long connectTimeout,Long readTimeout) throws Exception {
        final MediaType JSON = MediaType.parse(mediaType);
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(connectTimeout, TimeUnit.SECONDS)
                .readTimeout(readTimeout, TimeUnit.SECONDS)
                .build();
        RequestBody body = RequestBody.create(JSON, data);
        Request request = new Request.Builder().url(url).post(body).build();
        Response response = client.newCall(request).execute();
        return response.body().string();
    }
    /**
     * 发送httppost异步请求
     * @param url 请求URL
     * @param data 请求参数
     * @param callback 回调函数
     * @throws Exception
     */
    public static void httpPostAsync(String url, String data,Callback callback) throws Exception {
        final MediaType JSON = MediaType.parse(DEFAULT_MEDIATYPE);
        OkHttpClient client = new OkHttpClient.Builder()
                .retryOnConnectionFailure(false)
                .build();
        RequestBody body = RequestBody.create(JSON, data);
        Request request = new Request.Builder().url(url).post(body).build();
        client.newCall(request).enqueue(callback);
    }

    /**
     * 发送httppost异步请求
     * @param url 请求URL
     * @param data 请求参数
     * @param headersParams 请求头map
     * @param callback 回调函数
     * @throws Exception
     */
    public static void httpPostAsync(String url, String data,Map<String, String> headersParams,Callback callback) throws Exception {
        final MediaType JSON = MediaType.parse(DEFAULT_MEDIATYPE);
        Headers headers = setHeaders(headersParams);
        OkHttpClient client = new OkHttpClient.Builder()
                .retryOnConnectionFailure(false)
                .build();
        RequestBody body = RequestBody.create(JSON, data);
        Request request = new Request.Builder()
                .headers(headers)
                .url(url)
                .post(body)
                .build();
        client.newCall(request).enqueue(callback);
    }

    /**
     * 发送带自动重试的httppost异步请求
     * @param url 请求URL
     * @param data 请求参数
     * @param mediaType 请求类型
     * @param callback 回调函数
     * @throws Exception
     */
    public static void httpPostAsync(String url, String data,String mediaType,Callback callback) throws Exception {
        final MediaType JSON = MediaType.parse(mediaType);
        OkHttpClient client = new OkHttpClient.Builder()
                .retryOnConnectionFailure(false)
                .build();
        RequestBody body = RequestBody.create(JSON, data);
        Request request = new Request.Builder().url(url).post(body).build();
        client.newCall(request).enqueue(callback);
    }

    /**
     * 发送httppost异步请求
     * @param url 请求URL
     * @param data 请求参数
     * @param headersParams 请求头map
     * @param mediaType 请求类型
     * @param callback 回调函数
     * @throws Exception
     */
    public static void httpPostAsync(String url, String data,Map<String, String> headersParams,String mediaType,Callback callback) throws Exception {
        final MediaType JSON = MediaType.parse(mediaType);
        Headers headers = setHeaders(headersParams);
        OkHttpClient client = new OkHttpClient.Builder()
                .retryOnConnectionFailure(false)
                .build();
        RequestBody body = RequestBody.create(JSON, data);
        Request request = new Request.Builder()
                .headers(headers)
                .url(url)
                .post(body)
                .build();
        client.newCall(request).enqueue(callback);
    }

    /**
     * 发送带自动重试的httppost异步请求
     * @param url 请求URL
     * @param data 请求参数
     * @param callback 回调
     * @throws Exception
     */
    public static void httpPostAsyncRetry(String url, String data,Callback callback) throws Exception {
        final MediaType JSON = MediaType.parse(DEFAULT_MEDIATYPE);
        OkHttpClient client = new OkHttpClient.Builder()
                .retryOnConnectionFailure(true)
                .addInterceptor(new RetryIntercepter(DEFAULT_MAX_RETRY_NUM))
                .build();
        RequestBody body = RequestBody.create(JSON, data);
        Request request = new Request.Builder().url(url).post(body).build();
        client.newCall(request).enqueue(callback);
    }

    /**
     * 发送带自动重试的httppost异步请求
     * @param url 请求URL
     * @param data 请求参数
     * @param headersParams 请求头map
     * @param callback 回调函数
     * @throws Exception
     */
    public static void httpPostAsyncRetry(String url, String data,Map<String, String> headersParams,Callback callback) throws Exception {
        final MediaType JSON = MediaType.parse(DEFAULT_MEDIATYPE);
        Headers headers = setHeaders(headersParams);
        OkHttpClient client = new OkHttpClient.Builder()
                .retryOnConnectionFailure(true)
                .addInterceptor(new RetryIntercepter(DEFAULT_MAX_RETRY_NUM))
                .build();
        RequestBody body = RequestBody.create(JSON, data);
        Request request = new Request.Builder()
                .headers(headers)
                .url(url)
                .post(body)
                .build();
        client.newCall(request).enqueue(callback);
    }

    /**
     * 发送带自动重试的httppost异步请求
     * @param url 请求URL
     * @param data 请求参数
     * @param retryNum 重试次数
     * @param callback 回调函数
     * @throws Exception
     */
    public static void httpPostAsyncRetry(String url, String data,int retryNum,Callback callback) throws Exception {
        final MediaType JSON = MediaType.parse(DEFAULT_MEDIATYPE);
        OkHttpClient client = new OkHttpClient.Builder()
                .retryOnConnectionFailure(true)
                .addInterceptor(new RetryIntercepter(retryNum))
                .build();
        RequestBody body = RequestBody.create(JSON, data);
        Request request = new Request.Builder().url(url).post(body).build();
        client.newCall(request).enqueue(callback);
    }

    /**
     * 发送带自动重试的httppost异步请求
     * @param url 请求URL
     * @param data 请求参数
     * @param headersParams 请求头map
     * @param retryNum 重试次数
     * @param callback 回调函数
     * @throws Exception
     */
    public static void httpPostAsyncRetry(String url, String data,Map<String, String> headersParams,int retryNum,Callback callback) throws Exception {
        final MediaType JSON = MediaType.parse(DEFAULT_MEDIATYPE);
        Headers headers = setHeaders(headersParams);
        OkHttpClient client = new OkHttpClient.Builder()
                .retryOnConnectionFailure(true)
                .addInterceptor(new RetryIntercepter(retryNum))
                .build();
        RequestBody body = RequestBody.create(JSON, data);
        Request request = new Request.Builder()
                .headers(headers)
                .url(url)
                .post(body)
                .build();
        client.newCall(request).enqueue(callback);
    }

    /**
     * 发送带自动重试的httppost异步请求
     * @param url 请求URL
     * @param data 请求参数
     * @param mediaType 请求类型
     * @param callback 回调函数
     * @throws Exception
     */
    public static void httpPostAsyncRetry(String url, String data,String mediaType,Callback callback) throws Exception {
        final MediaType JSON = MediaType.parse(mediaType);
        OkHttpClient client = new OkHttpClient.Builder()
                .retryOnConnectionFailure(true)
                .addInterceptor(new RetryIntercepter(DEFAULT_MAX_RETRY_NUM))
                .build();
        RequestBody body = RequestBody.create(JSON, data);
        Request request = new Request.Builder().url(url).post(body).build();
        client.newCall(request).enqueue(callback);
    }

    /**
     * 发送带自动重试的httppost异步请求
     * @param url 请求URL
     * @param data 请求参数
     * @param headersParams 请求头map
     * @param callback 回调函数
     * @throws Exception
     */
    public static void httpPostAsyncRetry(String url, String data,Map<String, String> headersParams,String mediaType,Callback callback) throws Exception {
        final MediaType JSON = MediaType.parse(mediaType);
        Headers headers = setHeaders(headersParams);
        OkHttpClient client = new OkHttpClient.Builder()
                .retryOnConnectionFailure(true)
                .addInterceptor(new RetryIntercepter(DEFAULT_MAX_RETRY_NUM))
                .build();
        RequestBody body = RequestBody.create(JSON, data);
        Request request = new Request.Builder()
                .headers(headers)
                .url(url)
                .post(body)
                .build();
        client.newCall(request).enqueue(callback);
    }

    /**
     * 发送带自动重试的httppost异步请求
     * @param url 请求URL
     * @param data 请求参数
     * @param retryNum 重试次数
     * @param mediaType 请求类型
     * @param callback 回调函数
     * @throws Exception
     */
    public static void httpPostAsyncRetry(String url, String data,String mediaType,int retryNum,Callback callback) throws Exception {
        final MediaType JSON = MediaType.parse(mediaType);
        OkHttpClient client = new OkHttpClient.Builder()
                .retryOnConnectionFailure(true)
                .addInterceptor(new RetryIntercepter(retryNum))
                .build();
        RequestBody body = RequestBody.create(JSON, data);
        Request request = new Request.Builder().url(url).post(body).build();
        client.newCall(request).enqueue(callback);
    }

    /**
     * 发送带自动重试的httppost异步请求
     * @param url 请求URL
     * @param data 请求参数
     * @param headersParams 请求头map
     * @param mediaType 请求类型
     * @param retryNum 重试次数
     * @param callback 回调函数
     * @throws Exception
     */
    public static void httpPostAsyncRetry(String url, String data,Map<String, String> headersParams,String mediaType,int retryNum,Callback callback) throws Exception {
        final MediaType JSON = MediaType.parse(mediaType);
        Headers headers = setHeaders(headersParams);
        OkHttpClient client = new OkHttpClient.Builder()
                .retryOnConnectionFailure(true)
                .addInterceptor(new RetryIntercepter(retryNum))
                .build();
        RequestBody body = RequestBody.create(JSON, data);
        Request request = new Request.Builder()
                .headers(headers)
                .url(url)
                .post(body)
                .build();
        client.newCall(request).enqueue(callback);
    }

    /**
     * 带证书的HTTPS POST请求
     * @param url 请求URL
     * @param data 请求参数
     * @param headersParams 请求头map
     * @param filePath 证书地址
     * @param keyPwd 证书密码
     * @param mediaType 请求类型
     * @return
     * @throws Exception
     */
    public static String httpsPost(String url, String data,Map<String, String> headersParams,String filePath,String keyPwd,String mediaType) throws Exception {
        final MediaType JSON = MediaType.parse(mediaType);
        Headers headers = setHeaders(headersParams);

        //获取客户端证书
        KeyStore keyStore = getkeyStore("PKCS12", filePath, keyPwd);
        //获取信任证书库
        KeyStore trustStore = null;

        RequestBody body = RequestBody.create(JSON, data);
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(DEFAULT_CONNECT_TIMEOUT, TimeUnit.SECONDS)
                .readTimeout(DEFAULT_READ_TIMEOUT, TimeUnit.SECONDS)
                .sslSocketFactory(getSSLSocketFactory(keyStore, keyPwd, trustStore), new DefaultTrustManager())
                .hostnameVerifier((s, sslSession) -> true).build();
        Request request = new Request.Builder()
                .url(url).headers(headers).post(body).build();
        Response response = null;
        try {
            response = client.newCall(request).execute();
            System.out.println("post返回状态：" + response.code());
            return response.body().string();
        } finally{
            response.close();
        }
    }

    public static String getParams(Map<String, Object> params) {
        StringBuffer sb = new StringBuffer("?");
        if (ObjectConvertUtil.isNotEmpty(params)) {
            for (Map.Entry<String, Object> item : params.entrySet()) {
                Object value = item.getValue();
                if (ObjectConvertUtil.isNotEmpty(value)) {
                    sb.append("&");
                    sb.append(item.getKey());
                    sb.append("=");
                    sb.append(value);
                }
            }
            return sb.toString();
        } else {
            return "";
        }
    }

    /**
     * 设置header
     * @param headersParams
     * @return
     */
    public static Headers setHeaders(Map<String, String> headersParams) {
        Headers headers = null;
        okhttp3.Headers.Builder headersbuilder = new okhttp3.Headers.Builder();
        if (ObjectConvertUtil.isNotEmpty(headersParams)) {
            Iterator<String> iterator = headersParams.keySet().iterator();
            String key = "";
            while (iterator.hasNext()) {
                key = iterator.next();
                headersbuilder.add(key, headersParams.get(key));
            }
        }
        headers = headersbuilder.build();
        return headers;
    }

    /**
     * 获取证书
     *
     * @return
     */
    public static KeyStore getkeyStore(String type, String filePath, String password) throws IOException {
        KeyStore keySotre = null;
        FileInputStream in = null;
        try {
            keySotre = KeyStore.getInstance(type);
            in = new FileInputStream(new File(filePath));
            keySotre.load(in, password.toCharArray());
        } catch (Exception e) {
            log.error("==getkeyStore=="+e.getMessage());
        } finally {
            if (in != null) {
                in.close();
            }
        }
        return keySotre;
    }

    public static SSLSocketFactory getSSLSocketFactory(KeyStore keyStore, String keyStorePassword, KeyStore trustStore) throws Exception {
        KeyManager[] keyManagers = null;
        TrustManager[] trustManagers = null;
        if (keyStore != null) {
            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
            keyManagerFactory.init(keyStore, keyStorePassword.toCharArray());
            keyManagers = keyManagerFactory.getKeyManagers();
        }
        if (trustStore != null) {
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");
            trustManagerFactory.init(trustStore);
            trustManagers = trustManagerFactory.getTrustManagers();
        } else {
            trustManagers = new TrustManager[]{new DefaultTrustManager()};
        }

        //设置服务端支持的协议
        SSLContext context = SSLContext.getInstance("TLSv1.2");
        context.init(keyManagers, trustManagers, null);
        SSLSocketFactory sslFactory = context.getSocketFactory();

        return sslFactory;
    }

    private static class DefaultTrustManager implements X509TrustManager {
        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {

        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {

        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[]{};
        }
    }
}
